package com.adamfass.tractorbeam;

import com.adamfass.jmeutil.DebugUtil;
import com.adamfass.tractorbeam.gameupdatelogic.TractorBeamGameUpdateLogic;
import com.jme.math.Vector3f;
import com.jme.renderer.ColorRGBA;

public class AiFlyingSaucerController 
    implements Controller, GameEntity {
  private final GameObjectManager gameObjectManager;
  private FlyingSaucer flyingSaucer;
  private Goal goal;
  
  private Vector3f thrustVector;
  private boolean isTractorBeamOn;
  private PhysicsGameObject currentTarget;
  
  public AiFlyingSaucerController(
      GameObjectManager gameObjectManager) {
    this.gameObjectManager = gameObjectManager;
    gameObjectManager.addNonTargettableGameEntity(this);
  }
  
  public void setFlyingSaucer(FlyingSaucer flyingSaucer) {
    assert(this.flyingSaucer == null);
    this.flyingSaucer = flyingSaucer;
  }
  
  public void setGoal(Goal goal) {
    this.goal = goal;
  }
  
  private PhysicsGameObject getClosestTarget() {
    // Choose the closest target
    float minTargetDistanceSquared = Float.MAX_VALUE;
    PhysicsGameObject closestTarget = null;
    for (PhysicsGameObject target : 
         gameObjectManager.getTargettableObjects()) {
      float distanceSquared = target.getPosition().distanceSquared(
          flyingSaucer.getPosition());
      if (distanceSquared < minTargetDistanceSquared) {
        minTargetDistanceSquared = distanceSquared;
        closestTarget = target;
      }
    }   
    return closestTarget;
  }
  
  // ThrustVector and beam on/off must be set before the flying saucer asks
  // for the input values through the Controller interface.
  public void beforeUpdate() {
    final PhysicsGameObject ball = getClosestTarget();
    
    // If all balls go in goals simultaneously, there may be no closest target.
    // Just leave all the state the same.
    if (ball == null) {
      return;
    }
    
    final Vector3f ballPosition = ball.getPosition();
    final Vector3f goalPosition = goal.getWorldTranslation();
    final Vector3f saucerPosition = flyingSaucer.getPosition();
    
    final Vector3f ballToGoal =
      goalPosition.subtract(ballPosition);
    final Vector3f targetToGoalDirection = ballToGoal.normalize();
    Vector3f destination = 
      ballPosition.add(targetToGoalDirection.mult(
          TractorBeamGameUpdateLogic.PEAK_DISTANCE));
    
    final Vector3f ballToSaucerDirection = 
        saucerPosition.subtract(ballPosition).normalize(); 
    final Vector3f ballToSaucerPerpendicular = new Vector3f(
        -ballToSaucerDirection.y,
        ballToSaucerDirection.x,
        0);
    final Vector3f ballPerpendicularVelocity = 
        ballToSaucerPerpendicular.mult(
            getClosestTarget().getVelocity().dot(ballToSaucerPerpendicular));
    final Vector3f slingShift = ballPerpendicularVelocity.mult(-0.25f);
    destination = destination.add(slingShift);
    
    final Vector3f gravityShift = 
      Vector3f.UNIT_Y.mult(0.1f * ballToGoal.length());
    destination = destination.add(gravityShift);
    
    DebugUtil.showDebugMarker("destination", ColorRGBA.red, destination);
        
    thrustVector = destination.subtract(flyingSaucer.getPosition())
        .normalize();
    
    // If the current target is not the closest ball, or if it has just gone
    // into a goal, turn the beam off so that it can re-target next frame.
    if (isTractorBeamOn && (currentTarget.isDeleted() || currentTarget != ball)) {
      isTractorBeamOn = false;
      currentTarget = null;
    } else {  
      // The beam is off or the beam is on and the target is the same as last frame.
      // If the beam would pull the ball away from the goal, turn it off.
      if (Math.abs(ballToGoal.angleBetween(ballToSaucerDirection)) 
          > (Math.PI / 2)) {
        isTractorBeamOn = false;
        currentTarget = null;
      } else {
        isTractorBeamOn = true;
        currentTarget = ball;
      }
    }
  }
  
  public void update() {}
  
  public void cleanUp() {}
  
  public Vector3f getThrustVector() {
    return thrustVector;
  }
  
  public boolean isTractorBeamOn() {
    return isTractorBeamOn;
  }
  
  public boolean disruptorPodWasFired() {
    return false;
  }
  
  public void clearEventState() {}
}
